
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	NXEPSImageRepSub.m
 *
 *	A subclass of NXEPSImageRep. Enhanced to handle rotation
 *	and resources.
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 */

#import "NXEPSImageRepSub.h"

#import <appkit/Font.h>
#import <appkit/Panel.h>
#import <appkit/View.h>
#import <appkit/nextstd.h>
#import <appkit/defaults.h>

#import <objc/List.h>
#import <objc/Storage.h>
#import <objc/hashtable.h>
#import <dpsclient/wraps.h>
#import <stdio.h>

/*	Used only for demonstration purposes. */
#import "ImportApp.h"
#import "epsfwraps.h"

@implementation NXEPSImageRepSub

- free
{
	if (_tempName)
		NX_FREE(_tempName);

	return [super  free];
}

- writeIncludedFiles:storage;
{
	int			i, count,
				writelen, readlen, includelen, maxlen;

	char			*readdata, *startdata, *includedata;

	NXStream	*writestream, *readstream, *includestream;

	Inclusion		*inclusion;

	if (storage && [storage  count])
	{
		readlen = 0;	
		readdata = NULL;
		readstream = NULL;
		if (_fileName)
		{
			readstream = NXMapFile(_fileName, NX_READONLY);
			if (readstream)
				NXGetMemoryBuffer(readstream, &readdata, &readlen, &maxlen);
		}
		else
		{
			readdata = _memory;
			readlen = _epsLen;
		}

		if (readdata && readlen)
		{ 
			startdata = readdata;
			if (_fileName)
				writestream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
			else
				writestream = NXOpenMemory(NULL, 0, NX_READWRITE);
			if (writestream)
			{
				count = [storage  count];
				for (i = 0; i < count; i++)
				{
					inclusion = [storage  elementAt:i];
					if (inclusion->filename && inclusion->len)
					{
						writelen = inclusion->offset - (readdata - startdata);
						NXWrite(writestream, readdata, writelen);
						includestream = NXMapFile(inclusion->filename, NX_READONLY);
						if (includestream)
						{
							NXGetMemoryBuffer(includestream, &includedata,
								&includelen, &maxlen);
							NXWrite(writestream, includedata, includelen);
						}
						else
							NXWrite(writestream, readdata+inclusion->offset,
										inclusion->len);
						readdata += writelen + inclusion->len;
						readlen -= (writelen + inclusion->len);
					}
				}
				NXWrite(writestream, readdata, readlen);

				if (_fileName)
				{
					_tempName = NXCopyStringBuffer("/tmp/EpsfTempXXXXXX.eps");
					NXGetTempFilename(_tempName, 13);
					if (_tempName)
						NXSaveToFile(writestream, _tempName);
					NXCloseMemory(writestream, NX_FREEBUFFER);
				}
				else
				{
					NXGetMemoryBuffer(writestream, &_tempMemory,
								&_tempEpsLen, &maxlen);
					NXCloseMemory(writestream, NX_SAVEBUFFER);
				}
			}
		}

		if (readstream)
			NXCloseMemory(readstream, NX_FREEBUFFER);
	}

	return self;
}

/*
*	Check to see if the fonts are on the system and only flag
*	those not available.
*/
- checkFonts:(Resource *) resource  placeIn:(ResourceList *) resourceList
{
	id			font;

	int			i, count;

	if (resource && resourceList)
	{
		if (resource->states[RES_NEEDED])
			resourceList->types[RES_FONTS] = [resource->states[RES_NEEDED]  copy];
		else if (!resource->states[RES_SUPPLIED] && resource->states[RES_PRESENT])
			resourceList->types[RES_FONTS] = [resource->states[RES_PRESENT]  copy];

		/*
		*	There should be an easier way to check for font availability other
		*	than to create a font object but this will have to do for now.
		*/
		count = [resourceList->types[RES_FONTS]  count];
		for (i = count -1; i >= 0; i--)
		{
			font = [Font  newFont:(char *) [resourceList->types[RES_FONTS]  objectAt:i]  size:12];
			if (font)
			{
				[font  free];
				[resourceList->types[RES_FONTS]  removeObjectAt:i];
			}
		}

		if ([resourceList->types[RES_FONTS]  count] == 0)
		{
			[resourceList->types[RES_FONTS]  free];
			resourceList->types[RES_FONTS] = NULL;
		}
	}

	return self;
}

- checkFiles:(Resource *) resource  placeIn:(ResourceList *) resourceList
{
	int		i, count;

	FILE		*file;

	if (resource && resourceList)
	{
		if (resource->states[RES_NEEDED])
			resourceList->types[RES_FILES] = [resource->states[RES_NEEDED]  copy];

		/*
		*	Try to open the files. If available then remove from the list.
		*/
		count = [resourceList->types[RES_FILES]  count];
		for (i = count -1; i >= 0; i--)
		{
			file = fopen((char *) [resourceList->types[RES_FILES]  objectAt:i], "r");
			if (file)
			{
				fclose(file);
				[resourceList->types[RES_FILES]  removeObjectAt:i];
			}
		}

		if ([resourceList->types[RES_FILES]  count] == 0)
		{
			[resourceList->types[RES_FILES]  free];
			resourceList->types[RES_FILES] = NULL;
		}
	}

	return self;
}

- checkProcSets:(Resource *) resource  placeIn:(ResourceList *) resourceList
{
	if (resource && resourceList)
	{
		if (resource->states[RES_NEEDED])
			resourceList->types[RES_PROCSETS] = [resource->states[RES_NEEDED]  copy];
	}

	return self;
}

- checkPatterns:(Resource *) resource  placeIn:(ResourceList *) resourceList
{
	if (resource && resourceList)
	{
		if (resource->states[RES_NEEDED])
			resourceList->types[RES_PATTERNS] = [resource->states[RES_NEEDED]  copy];
	}

	return self;
}

- checkForms:(Resource *) resource  placeIn:(ResourceList *) resourceList
{
	if (resource && resourceList)
	{
		if (resource->states[RES_NEEDED])
			resourceList->types[RES_FORMS] = [resource->states[RES_NEEDED]  copy];
	}

	return self;
}

/*
*	Fills in the empty resources and inclusions array within the epsf structure. 
*/
- (int) getResources:(EpsfStruct *) epsf
{
	int			maxlen, error;

	NXStream	*stream;

	error = EPSF_INVALIDPS;
	if (epsf)
	{
		stream = NULL;
		epsf->filedata = NULL;
		if (_fileName)
		{
			stream = NXMapFile(_fileName, NX_READONLY);
			if (stream)
				NXGetMemoryBuffer(stream, &epsf->filedata, &epsf->filelen, &maxlen);
		}
		else
		{
			epsf->filedata = _memory;
			epsf->filelen = _epsLen;
		}
			
		if (epsf->filedata)
			error = ReadEpsf(epsf);

		if (stream)
			NXCloseMemory(stream, NX_FREEBUFFER);
	}	
	
	return error;
}

/*
*	Returns the list of unavailableResources.
*
*	Returns any resources except for fonts that are marked as needed.
*	Check to see if the fonts are on the system and flags only those not
*	available. As the other resources move into the realm of system
*	resources, this method can check the availability of these resources
*	as well.
*
*/
- checkResources:(ResourceList *) resourceList;
{
	int			error;

	EpsfStruct	epsf;

	bzero(resourceList, sizeof(ResourceList));
	error = [self  getResources:&epsf];
	if (!error)
	{
		[self  checkFonts:&epsf.resources[RES_FONTS]  placeIn:resourceList];
		[self  checkFiles:&epsf.resources[RES_FILES]  placeIn:resourceList];
		[self  checkProcSets:&epsf.resources[RES_PROCSETS]  placeIn:resourceList];
		[self  checkPatterns:&epsf.resources[RES_PATTERNS]  placeIn:resourceList];
		[self  checkForms:&epsf.resources[RES_FORMS]  placeIn:resourceList];

		[self  writeIncludedFiles:epsf.inclusions];

		return self;
	}
	else
		Notify("Import Error", ErrorEpsf(error));

	return nil;
}

/*
*	Used when printing. Add the resources used in this file
*	to the list passed in. Only adds the resources that are not
*	currently in the resourceDoc structure already.
*/
- addResources:(Resource *) resourceDoc  forFile:(NXAtom) fileName
{
	id			name;

	int			i, j, k, count;

	EpsfStruct	epsf;

	if (_fileName || fileName)
	{
		i = RES_SUPPLIED;
		if (_fileName)
		{
			name = (id) _fileName;
			if (NXDrawingStatus == NX_COPYING)
				i = RES_NEEDED;
		}
		else
			name = (id) fileName;

		if (!resourceDoc[RES_FILES].states[RES_PRESENT])
			resourceDoc[RES_FILES].states[RES_PRESENT] = [List  new];
		if (!resourceDoc[RES_FILES].states[RES_NEEDED])
			resourceDoc[RES_FILES].states[i] = [List  new];

		[resourceDoc[RES_FILES].states[RES_PRESENT]  addObjectIfAbsent:name];
		[resourceDoc[RES_FILES].states[i]  addObjectIfAbsent:name];
	}

	[self  getResources:&epsf];
	for (i = 0; i < RES_NUMTYPES; i++)
	{
		for (j = 0; j < RES_NUMSTATES; j++)
		{
			if (epsf.resources[i].states[j])
			{
				if (!resourceDoc[i].states[j])
					resourceDoc[i].states[j] = [List  new];

				count = [epsf.resources[i].states[j]  count];
				for (k = 0; k < count; k++)
					[resourceDoc[i].states[j]
							addObjectIfAbsent:[epsf.resources[i].states[j]  objectAt:k]];
			}
		}
	}
	
	return self;
}

/*
*	Overridden to handle any %%IncludeFile. A temporary file or
*	memory buffer has been written out that adds the included
*	files. This temporary file or memory pointer are swapped for the
*	real ones if they exist. The image is drawn and then they are
*	returned back. Their is probably a better way but without 
*	knowing the internals of how this class works its a tenuous
*	bet to find one.
*/
- (BOOL) draw
{
	BOOL	rc = YES;

	char		*data,
			*tempname,
			*tempmemory;

	int		templen, len, maxlen;

	NXStream	*stream;

	templen = 0;
	tempname = tempmemory = NULL;
	if (_tempName || _tempMemory)
	{
		if (_fileName)
		{
			tempname = _fileName;
			_fileName = _tempName;
		}
		else
		{
			tempmemory = _memory;
			templen = _epsLen;
			_memory = _tempMemory;
			_epsLen = _tempEpsLen;
		}
	}
	
	/*************************************************************
	*	The first case of the if-else statement is only to show
	*	what can happen when a separate context is not used.
	*	The imaging should be done with the draw method
	*	because it installs a separate context.
	**************************************************************/
	if (![NXApp  contextFlag])
	{
		PStranslate(-_bBoxOrigin.x, -_bBoxOrigin.y);
		if (_fileName && !_repFlags.dataLoaded)
		{
			stream = NXMapFile(_fileName, NX_READONLY);
			if (stream)
				NXGetMemoryBuffer(stream, &data, &len, &maxlen);
		}
		else
		{
			data = _memory;
			len = _epsLen;
		}

		PSWBeginEpsf();
		DPSWritePostScript(DPSGetCurrentContext(), data, len);
		PSWEndEpsf();
	}
	else
		rc = [super  draw];

	if (_tempName || _tempMemory)
	{
		if (_fileName)
		{
			_fileName = tempname;
		}
		else
		{
			_memory = tempmemory;
			_epsLen = templen;
		}
	}

	return rc;
}

/*
 *	Draws the EPS file. Positions it appropriately, scaling and rotating
 *	it according to the arguments passed in. The rotation is in degrees.
 */
- (BOOL) drawIn:(NXRect *) r  with:(float) rotation
{
	BOOL		error;

	NXRect		original;

	error = NO;
	if (NXDrawingStatus == NX_COPYING)
	{
		original.origin = _bBoxOrigin;
		original.size = size;
		if (_fileName)
		{
			WriteEpsfIllustratorBeg(r, &original, rotation, _fileName);
			WriteEpsfIllustratorInclude(_fileName);
			WriteEpsfIllustratorEnd();
		}
		else
		{
			WriteEpsfIllustratorBeg(r, &original, rotation, NULL);
			DPSWritePostScript(DPSGetCurrentContext(), _memory, _epsLen);
			WriteEpsfIllustratorEnd();
		}
	}
	else
	{
		PSgsave();
		PStranslate(r->origin.x, r->origin.y);
		PSrotate(rotation);
		PSscale(r->size.width/size.width, r->size.height/size.height);
		
		error = [self  draw];
		PSgrestore();
	}

	return error;
}

@end
